﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.ServiceModel;
using System.Security.Principal;
using SlamCms.SharePointServices.Authentication;
using SlamCms.SharePointServices.Lists;
using SlamCms.SharePointServices.Search;
using System.Web;
using SlamCms.SharePointServices.Configuration;

namespace SlamCms.SharePointServices
{
	public static class ServiceClients
	{
		private static Dictionary<string, Cookie> _fedAuthCookies = new Dictionary<string, Cookie>();

		public static HttpWebRequest CreateAuthenticatedRequest(string requestUrl)
		{
			var request = (HttpWebRequest)WebRequest.Create(requestUrl);

			Uri requestUri = new Uri(requestUrl);
			string credentialUri = String.Format("{0}://{1}", requestUri.Scheme, requestUri.Authority);

			bool usesClaimsAuthentication = false;
			ClaimsAuthenticationType claimsType = ClaimsAuthenticationType.Ntlm;
			if (!String.IsNullOrEmpty(ConfigurationManager.Environment.NetworkCredential.ClaimsAuthenticationType))
			{
				claimsType = (ClaimsAuthenticationType)Enum.Parse(typeof(ClaimsAuthenticationType), ConfigurationManager.Environment.NetworkCredential.ClaimsAuthenticationType);
				usesClaimsAuthentication = true;
			}

			if (usesClaimsAuthentication)
			{
				var cookieContainer = new CookieContainer();

				Cookie cookie = null;
				switch (claimsType)
				{
					case ClaimsAuthenticationType.Trusted:
						cookie = Credentials.FedAuthCookie;
						cookie.Domain = requestUri.Host;
						cookieContainer.Add(cookie);
						request.CookieContainer = cookieContainer;
						break;

					case ClaimsAuthenticationType.Ntlm:
						string fedAuthCookieKey = credentialUri + Credentials.NetworkCredential.UserName;

						if (_fedAuthCookies.ContainsKey(credentialUri + Credentials.NetworkCredential.UserName) && _fedAuthCookies[credentialUri + Credentials.NetworkCredential.UserName].TimeStamp.AddMinutes(5).CompareTo(DateTime.Now) > 0)
						{
							cookieContainer.Add(_fedAuthCookies[credentialUri + Credentials.NetworkCredential.UserName]);
							request.CookieContainer = cookieContainer;
						}
						else
						{
							var authRequest = (HttpWebRequest)WebRequest.Create(credentialUri + "/_windows/default.aspx?ReturnUrl=/_layouts/viewlsts.aspx");
							authRequest.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
							authRequest.PreAuthenticate = true;
							authRequest.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequested;
							var credentials = new CredentialCache();
							credentials.Add(new Uri(credentialUri), "NTLM", Credentials.NetworkCredential);
							var responseContainer = new CookieContainer();
							authRequest.CookieContainer = responseContainer;
							authRequest.Credentials = credentials;
							authRequest.KeepAlive = false;
							authRequest.Timeout = 5000;
							authRequest.ServicePoint.ConnectionLeaseTimeout = 5000;
							authRequest.ServicePoint.MaxIdleTime = 5000;
							authRequest.ServicePoint.ConnectionLimit = 50;

							using (var authResponse = authRequest.GetResponse() as HttpWebResponse)
							{
								if (authResponse != null && authResponse.StatusCode == HttpStatusCode.OK)
								{
									var responseCookies = responseContainer.GetCookies(authRequest.RequestUri);
									foreach (Cookie responseCookie in responseCookies)
									{
										if (responseCookie.Name.Equals("FedAuth"))
										{
											cookie = responseCookie;
											cookieContainer.Add(cookie);
											request.CookieContainer = cookieContainer;
											if (_fedAuthCookies.ContainsKey(credentialUri + Credentials.NetworkCredential.UserName))
												_fedAuthCookies[credentialUri + Credentials.NetworkCredential.UserName] = cookie;
											else
												_fedAuthCookies.Add(credentialUri + Credentials.NetworkCredential.UserName, cookie);
											break;
										}
									}
								}
								else
								{
									throw new Exception(String.Format("Authentication failed for user: {0}\\{1} at {2}. {3}", Credentials.NetworkCredential.Domain, Credentials.NetworkCredential.UserName, authRequest.RequestUri.ToString(), authResponse.StatusDescription));
								}
							}
						}
						break;

					case ClaimsAuthenticationType.Fba:
						var httpCookie = AuthenticationService.Login(requestUrl.Substring(0, requestUrl.LastIndexOf('/')), Credentials.NetworkCredential.UserName, Credentials.NetworkCredential.Password).Response;
						cookie = new Cookie(httpCookie.Name, httpCookie.Value, httpCookie.Path, httpCookie.Domain);
						cookie.Expires = httpCookie.Expires;
						cookie.HttpOnly = httpCookie.HttpOnly;
						cookieContainer.Add(cookie);
						request.CookieContainer = cookieContainer;
						break;

					default:
						request.PreAuthenticate = true;
						request.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequested;
						break;
				}
			}
			else
			{	
				request.PreAuthenticate = true;
				request.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequested;

				var credentials = new CredentialCache();
				credentials.Add(new Uri(credentialUri), "NTLM", Credentials.NetworkCredential);
				request.Credentials = credentials;
			}
			return request;
		}

		public static AuthenticationSoapClient GetAuthenticationServiceClient(string siteUrl)
		{
			BasicHttpBinding serviceBinding = new BasicHttpBinding();
			serviceBinding.ReceiveTimeout = new TimeSpan(0, 10, 0);
			serviceBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
			serviceBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
			serviceBinding.MaxReceivedMessageSize = 104857600;
			serviceBinding.ReaderQuotas.MaxBytesPerRead = 10485760;

			EndpointAddress endpointAddress = new EndpointAddress(siteUrl.TrimEnd('/') + "/_vti_bin/Authentication.asmx");
			AuthenticationSoapClient client = new AuthenticationSoapClient(serviceBinding, endpointAddress);
			client.ClientCredentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
			client.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
			return client;
		}

		public static ListsSoapClient GetListServiceClient(string siteUrl)
		{
			BasicHttpBinding serviceBinding = new BasicHttpBinding();
			serviceBinding.ReceiveTimeout = new TimeSpan(0, 10, 0);
			serviceBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
			serviceBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
			serviceBinding.MaxReceivedMessageSize = 104857600;
			serviceBinding.ReaderQuotas.MaxBytesPerRead = 10485760;

			EndpointAddress endpointAddress = new EndpointAddress(siteUrl.TrimEnd('/') + "/_vti_bin/Lists.asmx");
			ListsSoapClient client = new ListsSoapClient(serviceBinding, endpointAddress);
			client.ClientCredentials.Windows.ClientCredential = Credentials.NetworkCredential;
			client.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
			return client;
		}

		public static QueryServiceSoapClient GetSearchServiceClient(string siteUrl)
		{
			BasicHttpBinding serviceBinding = new BasicHttpBinding();
			serviceBinding.ReceiveTimeout = new TimeSpan(0, 10, 0);
			serviceBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
			serviceBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
			serviceBinding.MaxReceivedMessageSize = 104857600;
			serviceBinding.ReaderQuotas.MaxBytesPerRead = 10485760;

			EndpointAddress endpointAddress = new EndpointAddress(siteUrl.TrimEnd('/') + "/_vti_bin/search.asmx");
			QueryServiceSoapClient client = new QueryServiceSoapClient(serviceBinding, endpointAddress);
			client.ClientCredentials.Windows.ClientCredential = Credentials.NetworkCredential;
			client.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
			return client;
		}
	}
}
